﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.IO;

namespace Crude.Core
{
    public class AssemblyReference
    {
        public AssemblyReference(string name, string hintPath)
        {
            Name = name;
            HintPath = hintPath;
        }

        public string Name { get; set; }

        public string HintPath { get; set; }
    }

    public class ProjectReference
    {
        public string Name { get; set; }
        public string ProjectFileName { get; set; }
        public string ProjectDirectory { get; set; }
    }

    public class Project
    {
        XmlDocument m_doc;

        public Project(string projectFilePath)
        {
            ProjectFilePath = projectFilePath;
            LoadProject();
        }

        private void LoadProject()
        {
            m_doc = new XmlDocument();

            ProjectDirectory = new FileInfo(ProjectFilePath).Directory.FullName;

            m_doc.Load(ProjectFilePath);
        }

        public string ProjectFilePath
        {
            get;
            set;
        }

        public string ProjectDirectory
        {
            get;
            set;
        }

        public string PomFilePath
        {
            get
            {
                return string.Format("{0}\\{1}.pom.xml",
                ProjectDirectory, AssemblyName);
            }
        }

        public IList<string> GetOutputFiles()
        {
            return (from XmlNode node in m_doc.GetElementsByTagName("Content")
                    where node["CopyToOutputDirectory"] != null && 
                    (node["CopyToOutputDirectory"].InnerText == "PreserveNewest" ||
                    node["CopyToOutputDirectory"].InnerText == "Always")
                    select node.Attributes["Include"].Value).ToList();
        }

        public IList<ProjectReference> GetProjectReferences()
        {
            IEnumerable<XmlNode> nodes =
                (from XmlNode node in m_doc.GetElementsByTagName("ProjectReference")
                 select node);

            IList<ProjectReference> references = new List<ProjectReference>();

            foreach (XmlNode node in nodes)
            {
                string projectFilePath = node.Attributes["Include"].Value;

                FileInfo fileInfo = new FileInfo(string.Format("{0}\\{1}",
                    ProjectDirectory, projectFilePath));

                ProjectReference projectReference = new ProjectReference();
                projectReference.Name = node["Name"].InnerText;
                projectReference.ProjectDirectory = fileInfo.Directory.FullName;
                projectReference.ProjectFileName = fileInfo.Name;

                references.Add(projectReference);
            }

            return references;
        }

        public IList<AssemblyReference> GetReferences()
        {
            HashSet<string> frameworkAssemblies = FrameworkUtility.GetFrameworkAssemblies();

            IEnumerable<XmlNode> nodes =
                (from XmlNode node in m_doc.GetElementsByTagName("Reference")
                 where !frameworkAssemblies.Contains(node.Attributes["Include"].Value.ToLower())
                 select node);

            IList<AssemblyReference> references = new List<AssemblyReference>();

            foreach (XmlNode node in nodes)
            {
                string name = node.Attributes["Include"].Value;
                string hintPath = string.Empty;

                if (node["HintPath"] != null)
                {
                    hintPath = node["HintPath"].InnerText;
                }

                references.Add(new AssemblyReference(name, hintPath));
            }

            return references;
        }

        public bool IsImportLineExisted()
        {
            XmlNamespaceManager nsmgr = new XmlNamespaceManager(m_doc.NameTable);
            nsmgr.AddNamespace("b", m_doc.DocumentElement.NamespaceURI);

            var nodes = m_doc.DocumentElement.SelectNodes(@"//b:Import[@Project='$(CrudeBinPath)\Crude.targets']", nsmgr);
            

            return nodes.Count > 0;
        }

        public void AddImportLine()
        {
            XmlElement importElement = m_doc.CreateElement("Import", m_doc.DocumentElement.NamespaceURI);
            importElement.SetAttribute("Project", @"$(CrudeBinPath)\Crude.targets");

            m_doc.DocumentElement.AppendChild(importElement);
        }


        public string GetProjectVersion()
        {
            const string AssemblyLine = "[assembly: AssemblyVersion(\"";

            string assemblyInfoFile = string.Format("{0}\\Properties\\AssemblyInfo.cs", ProjectDirectory);

            if (File.Exists(assemblyInfoFile))
            {
                using (StreamReader streamReader = new StreamReader(assemblyInfoFile))
                {
                    string line = streamReader.ReadLine();

                    bool foundVersionLine = false;

                    while (!(foundVersionLine = line.StartsWith(AssemblyLine)) || streamReader.EndOfStream)
                    {
                        line = streamReader.ReadLine();
                    }

                    if (foundVersionLine)
                    {
                        line = line.Remove(0, AssemblyLine.Length);
                        line = line.Remove(line.Length - 3);

                        Version version;

                        if (Version.TryParse(line, out version))
                        {
                            return line;
                        }
                    }
                }
            }

            return "1.0.0.0";
        }

        public string GetProjectDescription()
        {
            const string AssemblyLine = "[assembly: AssemblyDescription(\"";

            string assemblyInfoFile = string.Format("{0}\\Properties\\AssemblyInfo.cs", ProjectDirectory);

            if (File.Exists(assemblyInfoFile))
            {
                using (StreamReader streamReader = new StreamReader(assemblyInfoFile))
                {

                    string line = streamReader.ReadLine();

                    bool foundDescriptionLine = false;

                    while (!(foundDescriptionLine = line.StartsWith(AssemblyLine)) || streamReader.EndOfStream)
                    {
                        line = streamReader.ReadLine();
                    }

                    if (foundDescriptionLine)
                    {
                        line = line.Remove(0, AssemblyLine.Length);
                        line = line.Remove(line.Length - 3);

                        return line;
                    }                    
                }
            }

            return "";
        }

        public void Save()
        {
            m_doc.Save(ProjectFilePath);
        }

        public string FileType
        {
            get
            {
                return m_doc.GetElementsByTagName("OutputType")[0].InnerText;
            }
        }

        public string AssemblyName
        {
            get
            {
                return m_doc.GetElementsByTagName("AssemblyName")[0].InnerText;
            }
        }

        public string OutputType
        {
            get
            {
                return m_doc.GetElementsByTagName("OutputType")[0].InnerText;
            }
        }
    }
}
